<?php
/**
 * Description:民生支付sdk
 * User: wuhao
 * Date: 2017-06-12
 * Time: 17:19
 */
namespace Business\CMBC;

class CmbcSDK
{
    # 配置支付类型属性
    const API_WXQRCODE      = 'API_WXQRCODE';//微信正扫
    //const API_WXSCAN        = 'API_WXSCAN';  //微信反扫
    //const H5_WXJSAPI        = 'H5_WXJSAPI';  //微信公众号

    const API_ZFBQRCODE     = 'API_ZFBQRCODE';//支付宝正扫
    //const API_ZFBSCAN       = 'API_ZFBSCAN';//支付宝反扫
    //const H5_ZFBJSAPI       = 'H5_ZFBJSAPI';//支付宝服务窗
    private $toolbox;
    private $config = [];

    public function __construct()
    {
        $this->toolbox = new ToolBox();
        $this->check_config();
    }

    /**
     * 检测配置信息
     * @return void
     */
    private function check_config()
    {
        //读取配置
        $config = parse_ini_file('/var/www/html/zft/extend/Business/CMBC/cmbc_pay_config.ini', TRUE);
        $chk_config = [
            'cert_bank_path'    => '民生银行公钥证书',
            'cert_private_path' => '商户私钥证书',
            'cert_public_path'  => '商户公钥证书',
            'cert_password'     => '私钥证书密码',
            'platform_id'       => '接入平台号',
            'merchantno'        => '统一商户号',
            'order_submit_api'  => '统一下单 API',
            'order_query_api'   => '支付结果查询 API',
            'order_cancel_api'   => '退款 API',
        ];
        foreach ($chk_config as $key=>$desc) {
            if (!isset($config[ENV][$key]) || $config[ENV][$key]=='') {
                $msg = "读取配置错误:{$desc}配置有误";
                exit('{"status":"fail","msg":"'.$msg.'","data":""}');
            }
        }
        $this->config = $config[ENV];
    }

    /**
     * curl
     * @param $url
     * @param $postData
     * @return mixed|string
     */
    private function post($url, $postData)
    {
        $curl = curl_init();
        curl_setopt_array($curl, array(
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => "",
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => "POST",
            CURLOPT_POSTFIELDS => $postData,
            CURLOPT_HTTPHEADER => array(
                "cache-control: no-cache",
                "content-type: application/json",
            ),
        ));
        $response = curl_exec($curl);
        $err = curl_error($curl);
        curl_close($curl);
       // dd($response, $err);
        if ($err) {
            return "cURL Error #:" . $err;
        } else {
            return $response;
        }
    }
    /**
     * 公共请求方法
     *
     * @param string $url 请求接口地址
     * @param string $businessContext base64_encode过的字符串
     * @return bool|mixed
     */
    private function Request($url, $businessContext)
    {
        $post_params = json_encode([
            'businessContext'   => $businessContext,
            'merchantNo'        => $this->config['merchantno'],
            'merchantSeq'       => '',
            'reserve1'          =>'',
            'reserve2'          =>'',
            'reserve3'          =>'',
            'reserve4'          =>'',
            'reserve5'          =>'',
            //'reserve6'          =>'',
            'reserveJson'       =>'',
            'securityType'      =>'',
            'sessionId'         =>'',
            'source'            =>'',
            'transCode'         =>'',
            'transDate'         =>'',
            'transTime'         =>'',
            'version'           =>'',
        ]);
        //zft_log('wepay_cmbc/debug', '生成请求报文:' . $post_params);
        //return curl_post($url, $post_params,80,30,'/api/cmbc_pay', ['content-type: application/json']);
        return self::post($url, $post_params);
    }

    /**
     * 订单支付加密数据
     * API文档:http://wxpay.cmbc.com.cn/cmbcpaydemo/MainServlet?name=toSDKOrder.do
     * DEMO:http://wxpay.cmbc.com.cn/cmbcpaydemo/MainServlet?name=toApi.do
     * @param int $amount  交易金额，以分为单位
     * @param string $orderInfo 订单信息[商户订单内容，用于在民生页面反显]
     * @param string $merchantSeq 订单流水[商户流水(须保证唯一),建议是商户平台号+8位日期+商户自定的订单号]
     * @param string $remark 备注
     * @param string $notifyUrl 支付成功异步通知地址
     //* @param string $subOpenid 微信粉丝OPENID
     * @param string $selectTradeType 支付类型
     * @return array ['code'=>200成功,'data'=>,'msg'=>'错误描述']
     */
    public function Pay($amount,$orderInfo,$merchantSeq , $notifyUrl, $remark='', $selectTradeType='API_WXQRCODE')
    {
        $transDate = date('Ymd');//订单日期[20170120]
        $transTime = date('YmdHis').$this->toolbox->getMillisecond();//订单时间[yyyyMMddHHmmssSSS:20170120102301774]
        $content = [
            'platformId'        => $this->config['platform_id'],
            'merchantNo'        => $this->config['merchantno'],
            'amount'            => $amount,
            'selectTradeType'   => $selectTradeType,
            'merchantSeq'       => $merchantSeq,
            'notifyUrl'         => $notifyUrl,
            'orderInfo'         => $orderInfo,
            'remark'            => $remark,
            'transDate'         => $transDate,
            'transTime'         => $transTime,
        ];
/*        if ($selectTradeType==self::H5_WXJSAPI) {
            $content['subAppId']  = PFT_WECHAT_APPID;
            $content['subOpenId'] = $subOpenid;
        }*/

        //$result = self::common_request($content, $this->config['order_submit_api']);
        $json = json_encode($content, JSON_UNESCAPED_UNICODE);
        zft_log('cmbc/debug', 'json:' . $json);
        $base64json =  base64_encode($json);
        //获取消息签名
        $sign_result = $this->toolbox->getSign($base64json, $this->config['cert_private_path'], $this->config['cert_password']);

        if ($sign_result['return_code']=='SUCCESS') {
            $arr = ['sign'=>$sign_result['data'], 'body'=>$json];
            $encrypt_content_before = json_encode($arr);
            //生成数字信封
            $encrypt_content_after  = $this->toolbox->envelopeMessage($encrypt_content_before, $this->config['cert_bank_path']);
            if ($encrypt_content_after['return_code']===0) {
                return $encrypt_content_after;
            }

            //封装消息并发送curl
            $json_result = self::Request($this->config['order_submit_api'], $encrypt_content_after['data']);
            $arr_result  = json_decode($json_result, true);
            if ($arr_result['businessContext']=='') {
                return ['return_code'=>'FAIL','return_msg'=>$arr_result['gateReturnMessage']];
            }
            $business = $this->toolbox->openMessage($arr_result['businessContext'], $this->config['cert_private_path'], $this->config['cert_password']);
            if ($business['return_code']!=='SUCCESS') {
                return $business;
            }
            $dec = base64_decode($business['data']);
            $business_data = json_decode($dec, true);
            zft_log('cmbc/debug', '响应明文:' . $dec);
            //TODO::check sign
            $chk_sign = $this->toolbox->checkSign($base64json, $this->config['cert_bank_path'], $business_data['sign']);
            if ($chk_sign['return_code']=='SUCCESS') {
                $pay_result = json_decode($business_data['body'], true);
                if ($pay_result['tradeStatus']=='E') {
                    return ['return_code'=>0, 'return_msg'=>'银行对接支付失败'];
                }

                //TODO::get payinfo
                $jsApiObj = [];
/*                if ($selectTradeType==self::H5_WXJSAPI) {
                    $payInfo = str_replace('|', '&', $pay_result['payInfo']);
                    parse_str($payInfo, $jsApiObj);
                    //$jsApiObj["package"] = "prepay_id={$jsApiObj['prepay_id']}";
                    $jsApiObj["signType"] = "MD5";
                    $jsApiObj["package"] = "prepay_id={$jsApiObj['prepayId']}";
                    unset($jsApiObj['prepayId']);
                    return ['return_code'=>'SUCCESS', 'data'=>$jsApiObj];
                }*/
                if ($selectTradeType==self::API_WXQRCODE || $selectTradeType==self::API_ZFBQRCODE) {
                    return ['return_code'=>'SUCCESS', 'code_url'=>base64_decode($pay_result['payInfo'])];
                }
            } else {
                return $chk_sign;
            }
        }
        return $sign_result;
    }

    /**
     * 处理交易异步通知结果
     *
     * @param string $raw_post_data 原始数据
     * @return array
     */
    public function PayNotify($raw_post_data)
    {
        $context = json_decode($raw_post_data);
        if (!$context) return ['return_code'=>0, 'return_msg'=>'接收数据为空'];
        $business = $this->toolbox->openMessage($context->context, $this->config['cert_private_path'], $this->config['cert_password']);
        $dec = base64_decode($business['data']);
        //pft_log('wepay_cmbc/notify', 'Notify返回密文解密:' .$business['data']);
        $business_data = json_decode($dec, true);
        zft_log('cmbc/notify', 'Notify响应明文:' . $dec);
        //TODO::check sign
        $chk_sign = $this->toolbox->checkSign($context->context, $this->config['cert_bank_path'], $business_data['sign']);
        if ($chk_sign['return_code']=='SUCCESS') {
            $pay_result = json_decode($business_data['body'], true);
            return ['return_code'=>'SUCCESS','data'=>$pay_result];
        }
        return ['return_code'=>0,'return_msg'=>'验证签名失败'];
    }

    /**
     * 订单查询
     *
     * @param string $orderId 原交易商户订单号
     * @param int $tradeType 1:支付，2:退款
     * @param string $orgvoucherNo 原交易凭证号,当查询类型为退款时必须输入
     * @param string $reserve
     * @return array
     */
    public function Query($orderId, $tradeType, $orgvoucherNo='', $reserve='')
    {
        $content = [
            'platformId'   => $this->config['platform_id'],
            'merchantNo'   => $this->config['merchantno'],
            'merchantSeq'  => $orderId,
            'tradeType'    => $tradeType,
            'orgvoucherNo' => $orgvoucherNo,
            'reserve'      => $reserve,
        ];
        $result = self::common_request($content, $this->config['order_query_api']);
        return $result;
    }
    private function common_request(Array $content, $url)
    {
        $json = json_encode($content, JSON_UNESCAPED_UNICODE);
        $base64json =  base64_encode($json);
        zft_log('cmbc/debug', 'json:' . $json);
        $sign_result = $this->toolbox->getSign($base64json, $this->config['cert_private_path'], $this->config['cert_password']);
        if ($sign_result['return_code']=='SUCCESS') {
            $arr = ['sign'=>$sign_result['data'], 'body'=>$json];
            $encrypt_content_before = json_encode($arr);
            $encrypt_content_after  = $this->toolbox->envelopeMessage($encrypt_content_before, $this->config['cert_bank_path']);
            if ($encrypt_content_after['return_code']===0) {
                return $encrypt_content_after;
            }
            $json_result = self::Request($url, $encrypt_content_after['data']);
            zft_log('cmbc/debug', '获取返回密文:' . $json_result);
            $arr_result  = json_decode($json_result, true);
            if ($arr_result['businessContext']=='') {
                return ['return_code'=>'FAIL','return_msg'=>$arr_result['gateReturnMessage']];
            }
            $business = $this->toolbox->openMessage($arr_result['businessContext'], $this->config['cert_private_path'], $this->config['cert_password']);
            if ($business['return_code']!=='SUCCESS') {
                return $business;
            }
            $dec = base64_decode($business['data']);
            $business_data = json_decode($dec, true);
            zft_log('cmbc/debug',  '返回密文解密:' .$business['data'].';响应明文:' . $dec);
            //TODO::check sign
            $chk_sign = $this->toolbox->checkSign($base64json, $this->config['cert_bank_path'], $business_data['sign']);
            if ($chk_sign['return_code']=='SUCCESS') {
                $pay_result = json_decode($business_data['body'], true);
                return $pay_result;
            }
            return $chk_sign;
        }
        return $sign_result;
    }


    /**
     * 退款
     *
     * @param string $orderId 商户订单号
     * @param string $amount 退款金额,单位“分”
     * @param string $orderNote 退款说明
     * @param string $reserve 备注字段
     * @return array|mixed
     */
    public function Refund($orderId, $amount, $orderNote='',$reserve ='')
    {
        $content = [
            'platformId'   => $this->config['platform_id'],
            'merchantNo'   => $this->config['merchantno'],
            'merchantSeq'  => $orderId,
            'orderAmount'  => $amount,
            'orderNote'    => $orderNote,
            'reserve'      => $reserve,
        ];
        //print_r($content);
        $result = self::common_request($content, $this->config['order_cancel_api']);
        return $result;
    }
}